// 8085Display.cpp : implementation file
//

#include "stdafx.h"
#include "8085.h"
#include "8085Display.h"
#include "MainFrm.h"
#include <afxpriv.h> // for WM_SETMESSAGESTRING
#include "MyNumericBox.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// C8085Display dialog


C8085Display::C8085Display(CWnd* pParent /*=NULL*/)
	: CDialog(C8085Display::IDD, pParent)
{
	//{{AFX_DATA_INIT(C8085Display)
	m_szInstructions = _T("");
	m_szMemoryDump = _T("");
	m_szRegisters = _T("");
	//}}AFX_DATA_INIT
    Init();
}


void C8085Display::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(C8085Display)
	DDX_Control(pDX, IDC_PORT_TEXT, m_szPortsText);
	DDX_Control(pDX, IDC_PORTDUMP, m_PortDump);
	DDX_Control(pDX, IDC_STACK_TEXT, m_cStackText);
	DDX_Control(pDX, IDC_STACKDUMP, m_StackDump);
	DDX_Control(pDX, IDC_REGISTER_TEXT, m_cRegisterText);
	DDX_Control(pDX, IDC_MEMDUMP_TEXT, m_cMemDumpText);
	DDX_Control(pDX, IDC_FLAG_TEXT, m_cFlagText);
	DDX_Control(pDX, IDC_INSTRUCTION_TEXT, m_cInstructionText);
	DDX_Control(pDX, IDC_MEMDUMP, m_MemoryDump);
	DDX_Control(pDX, IDC_REGISTERS, m_Registers);
	DDX_Control(pDX, IDC_FLAGS, m_Flags);
	DDX_Control(pDX, IDC_INSTRUCTIONS, m_Instructions);
	DDX_LBString(pDX, IDC_INSTRUCTIONS, m_szInstructions);
	DDX_LBString(pDX, IDC_MEMDUMP, m_szMemoryDump);
	DDX_LBString(pDX, IDC_REGISTERS, m_szRegisters);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(C8085Display, CDialog)
	//{{AFX_MSG_MAP(C8085Display)
	ON_COMMAND(ID_BUTTON_REFRESH, OnButtonRefresh)
	ON_COMMAND(ID_BUTTON_ANIMATE, OnButtonAnimate)
	ON_COMMAND(ID_BUTTON_STOP,          OnButtonStop)
	ON_COMMAND(ID_BUTTON_RUN,           OnButtonRun)
    ON_COMMAND(ID_BUTTON_STEP_INTO,     OnButtonStepInto)
	ON_COMMAND(ID_BUTTON_STEP_OVER,     OnButtonStepOver)
	ON_COMMAND(ID_BUTTON_STEP_OUT,      OnButtonStepOut)
	ON_COMMAND(ID_BUTTON_RUNTO,         OnButtonRunto)
	ON_COMMAND(ID_BUTTON_QUIT_SIMULATOR, OnButtonQuitSimulator)
	ON_WM_CREATE()
	ON_COMMAND(ID_BUTTON_SHOW_LED, OnButtonShowLed)
	ON_UPDATE_COMMAND_UI(ID_BUTTON_STOP, OnUpdateButtonStop)
	ON_WM_CLOSE()
	ON_COMMAND(ID_BUTTON_TOGGLE_BKPT, OnButtonToggleBkpt)
	ON_COMMAND(ID_BUTTON_ENABLE_DISABLE_BKPT, OnButtonEnableDisableBkpt)
    ON_MESSAGE(ID_LED_DISPLAY_CLOSED,ToggleLedWindow)
    ON_MESSAGE(ID_REFRESH_STACK_BOX,RefreshStackBox)
    ON_MESSAGE(ID_REFRESH_INSTRUCTION_BOX,RefreshInstructionBox)
    ON_MESSAGE(MY_WM_KEYDOWN,OnVKeyDown)
    ON_MESSAGE(MY_WM_KEYUP,OnVKeyUp)
	ON_COMMAND(ID_ACCEL_CLOSE_SIMULATION, OnAccelCloseSimulation)
	//}}AFX_MSG_MAP
    ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
    ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// C8085Display message handlers

void C8085Display::Init()
{
    m_nDelayValue = 20;
    m_szWindowText = "8085 Instruction Set Simulator";
    m_bLEDButtonState = FALSE; // initially the LED display is not shown.
    m_bTempBkPt = FALSE;
    m_bShiftDown = m_bCtrlDown = FALSE;
}

void C8085Display::Refresh()
{
    DisplayMemoryDump();
    DisplayRegisters();
    DisplayFlags();
    DisplayInstructions();
    DisplayStackDump();
    DisplayPortDump();
    m_pLedDisplay->Refresh();
}

void C8085Display::PostNcDestroy() 
{
	// TODO: Add your specialized code here and/or call the base class
	
    delete this;
	//CDialog::PostNcDestroy();
}

void C8085Display::OnOK() 
{
	// TODO: Add extra validation here
    GetDocument()->SetDisplayOpen(FALSE);
    //m_pLedDisplay->PostMessage(WM_CLOSE);
    m_pLedDisplay->DestroyWindow();
    // reset everything because we might in the middle of a system call
    // which is reading from the keyboard
    CMyNumericBox::DecrementRead();
    // terminate the thread if it is still running
    TerminateThread(m_hThreadHandle,-1);
	CDialog::DestroyWindow();
}


BOOL C8085Display::OnInitDialog() 
{
    CDialog::OnInitDialog();
    SetReadyText();
	// TODO: Add extra initialization here

    m_cToolBar.CreateEx(this, TBSTYLE_FLAT);
    ToggleLedWindow();
        //, WS_CHILD | WS_VISIBLE | CBRS_BOTTOM
		//| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
    m_cToolBar.LoadToolBar(IDR_DEBUG_TOOLBAR);
    m_cToolBar.ShowWindow(SW_SHOW);
    m_cToolBar.SetBarStyle( CBRS_ALIGN_RIGHT| CBRS_TOOLTIPS | CBRS_FLYBY);
    RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);
    ChangeButtonState();
    GetDocument()->SetDisplayOpen();
    ((CMy8085App *)AfxGetApp())->m_h8085Dialog = m_hWnd;
	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}


BOOL C8085Display::OnCommand(WPARAM wParam, LPARAM lParam) 
{
	// TODO: Add your specialized code here and/or call the base class
    CWnd *p = GetFocus();

     // dont do anything if simulator is running.
    if(!GetDocument()->IgnoreInputs() && (wParam==IDOK))
    {
        ((CMyAddressListBox *)p)->EnterValidData();
        //Refresh();
        return TRUE;
    }
	return CDialog::OnCommand(wParam, lParam);
}

void C8085Display::OnButtonRefresh() 
{
	// TODO: Add your control notification handler code here
	Adjust();
    Refresh();	
}

void C8085Display::OnButtonAnimate() 
{
	// TODO: Add your control notification handler code here
    Animate(400);
}

void C8085Display::SimulateOneInstruction()
{
    Get8085Object()->SimulateOneInstruction();
}


// call this function to make sure that the portion of list box always displays IP
void C8085Display::Adjust()
{
    m_Instructions.Adjust(Get8085Object()->GetIP());
}

DWORD WINAPI AnimateRoutine(LPVOID v)
{
    C8085Display * thisObject = (C8085Display *)v;
    try
    {
        while(thisObject->IsAnimating())
        {
            thisObject->SimulateOneInstruction();
            // a zero value for animation delay will mean 'run' 
            // instead of animation
            if(thisObject->GetAnimationDelay())
            {
                thisObject->Adjust();
                thisObject->Refresh();
                Sleep(thisObject->GetAnimationDelay());
            }
        }
        thisObject->CleanupUserInterface(); // control may never come here
    }
    catch (CMyException& cm)
    {
        thisObject->CleanUp(cm);
    }
    return 0;
}


int C8085Display::GetAnimationDelay()
{
    return m_nDelayValue;
}

BOOL C8085Display::CreateAll(UINT nIDTemplate, CWnd* pParentWnd )
{
    int nRet = Create(nIDTemplate,pParentWnd);
/*	m_MemoryDump.Attach(GetSafeHwnd());
	m_Registers.Attach(GetSafeHwnd());
	m_Flags.Attach(GetSafeHwnd());
	m_Instructions.Attach(GetSafeHwnd());
*/
    return nRet;
}

void C8085Display::Animate(int nDelay)
{
  SECURITY_ATTRIBUTES attr;  // pointer to security attributes
  attr.nLength = sizeof(SECURITY_ATTRIBUTES);
  attr.lpSecurityDescriptor = NULL;
  attr.bInheritHandle = FALSE;

  SetRunningText();
  GetDocument()->SetAnimating(TRUE);
  GetDocument()->SetIgnoreInputs(TRUE);
  m_nDelayValue = nDelay;   // set a value of zero to make it run instead of animation
  m_Instructions.SetFocus();
  ChangeButtonState();
  m_hThreadHandle = CreateThread(&attr,0,AnimateRoutine,this,0,&m_ThreadID);
}


// called by AnimateRoutine when an exception arises during the simulation
void C8085Display::CleanUp(CMyException& cm)
{
    CleanupUserInterface();
    Get8085Object()->SetExceptionOnRet(FALSE);
    GetDocument()->SetAnimating(FALSE);
    ChangeButtonState();
    if(cm.IsBreakPoint() || cm.IsRetExecuted())
	{
		//delete cm;
		return;
	}
    GetDocument()->RemoveBkPtFromCode();
    Adjust();
    Refresh();
    MessageBox(cm.GetErrorMessage(),"Exception",MB_OK);
	//delete cm;
}

void C8085Display::CleanupUserInterface()
{
    GetDocument()->RemoveBkPtFromCode();
    RemoveTempBkPt();  
    GetDocument()->SetIgnoreInputs(FALSE);
    Adjust();
    Refresh();
    SetReadyText();
    try
    {
        GetView()->ShowCurrentPosition();
    }
    catch (CMyException& cm)
    {
        GetView()->StatusBarMessage(cm.GetErrorMessage());
        return;
    }
}


BOOL C8085Display::OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult)
{
     ASSERT(pNMHDR->code == TTN_NEEDTEXTA || pNMHDR->code == TTN_NEEDTEXTW);

     // if there is a top level routing frame then let it handle the message
     if (GetRoutingFrame() != NULL) return FALSE;

     // to be thorough we will need to handle UNICODE versions of the message also !!
     TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
     TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
     //TCHAR szFullText[512];
     CString strTipText;
     UINT nID = pNMHDR->idFrom;

     if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||
         pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))
     {
          // idFrom is actually the HWND of the tool 
          nID = ::GetDlgCtrlID((HWND)nID);
     }

     if (nID != 0) // will be zero on a separator
     {
          strTipText.LoadString(nID);
          int nTillNewLine = strTipText.Find('\n');
          if(nTillNewLine<0)
              nTillNewLine=0;
          int nLen= strTipText.GetLength()-nTillNewLine;
          //strTipText=szFullText;

#ifndef _UNICODE
          if (pNMHDR->code == TTN_NEEDTEXTA)
          {
               lstrcpyn(pTTTA->szText, strTipText.Right(nLen-1),nLen);
          }
          else
          {
               _mbstowcsz(pTTTW->szText,strTipText.Right(nLen-1),nLen);
          }
#else
          if (pNMHDR->code == TTN_NEEDTEXTA)
          {
               _wcstombsz(pTTTA->szText,strTipText.Right(nLen-1),nLen);
          }
          else
          {
               lstrcpyn(pTTTW->szText,strTipText.Right(nLen-1),nLen);
          }
#endif

          *pResult = 0;
          //to get status bar prompts in addition to tool tips
          CMainFrame *mainFrame = (CMainFrame *) AfxGetApp()->m_pMainWnd;
          GetOwner()->SendMessage(WM_SETMESSAGESTRING, pNMHDR->idFrom);
          // bring the tooltip window above other popup windows
          ::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0,SWP_NOACTIVATE|
               SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER); return TRUE;
     }
     return TRUE;
} 

void C8085Display::CloseDialog()
{
    OnOK();
}

void C8085Display::OnButtonStepInto() 
{
	// TODO: Add your command handler code here
    StepInto();
}

void C8085Display::OnButtonStepOver() 
{
	// TODO: Add your command handler code here
    if(IsAnimating()) return;
    StepOver();
}

void C8085Display::OnButtonStepOut() 
{
	// TODO: Add your command handler code here
    if(IsAnimating()) return;
    StepOut();
}

void C8085Display::OnButtonRunto() 
{
	// TODO: Add your command handler code here
    if(IsAnimating()) return;
    RunToCursor();
}

void C8085Display::OnButtonRun() 
{
	// TODO: Add your control notification handler code here
    if(IsAnimating()) return;
    Run();
}

void C8085Display::OnButtonQuitSimulator() 
{
	// TODO: Add your command handler code here
    if(IsAnimating()) return;
	GetView()->Set8085DisplayDialogWindowPosition( );	// save for the next invokation
    OnOK();		
}

void C8085Display::OnButtonStop() 
{
	// TODO: Add your control notification handler code here
    if(!IsAnimating()) return;
    GetDocument()->SetAnimating(FALSE);
    ChangeButtonState();
    Refresh();
    //TerminateThread(m_ThreadID,0);	
}

void C8085Display::SetReadyText()
{
    CString title;
    title.Format(" [%s]  [READY]",GetDocument()->GetTitle());
    SetWindowText(title);
    title += "   LED Display ";
    m_pLedDisplay->SetWindowText(title);
}

void C8085Display::SetRunningText()
{
    CString title;
    title.Format(" [%s]  [RUNNING]",GetDocument()->GetTitle());
    SetWindowText(title);
    title += "   LED Display ";
    m_pLedDisplay->SetWindowText(title);
}

int C8085Display::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CDialog::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	// TODO: Add your specialized creation code here
    m_pLedDisplay = new CLedDisplay(this); //GetParent());
    m_pLedDisplay->Create(CLedDisplay::IDD,GetParent());
    m_pLedDisplay->ShowWindow(m_bLEDButtonState);
	return 0;
}

void C8085Display::OnButtonShowLed() 
{
	// TODO: Add your command handler code here
    ToggleLedWindow();
}

void C8085Display::OnUpdateButtonStop(CCmdUI* pCmdUI) 
{
	// TODO: Add your command update UI handler code here
    pCmdUI->Enable(0);	
}

void C8085Display::StepOver()
{
    word addr = Get8085Object()->GetIP();
    AddTempBkPt(addr+Get8085Object()->GetInstructionLenAt(addr));
    Run();
}

// Add a temporary break point which is used by Step over and run to cursor
void C8085Display::AddTempBkPt(word addr)
{
    m_bTempBkPt = TRUE;
    m_wAddr = addr;
    GetDocument()->AddBkPt(addr);
}

// Remove the temporary break point set by Step over and run to cursor
void C8085Display::RemoveTempBkPt()
{
    if(!m_bTempBkPt) return;
    m_bTempBkPt = FALSE;
    GetDocument()->DeleteBkPt(m_wAddr);
}

void C8085Display::RunToCursor()
{
    word addr = AddressAtIndex( );
    RunToCursor(addr);
}

void C8085Display::RunToCursor(word addr)
{
    AddTempBkPt(addr);
    Run();
}

void C8085Display::Run()
{
    // execute one instruction then insert the breakpoints 
    StepInto(FALSE); // if this is not done a breakpoint exception will again 
                     // be generated at the same IP.
    // now insert all the breakpoints
    if(!m_bExceptionCaught)
    {
/*
		OneBreakPoint* pt;

		for(pt=FirstBreakPoint(); pt ; pt = NextBreakPoint() ) {
	        Get8085Object()->WriteMem(pt->address,pt->data);
		}
*/
        GetDocument()->InsertBkPtInCode();
        Animate(0);
    }
}

void C8085Display::StepOut()
{
    Get8085Object()->SetExceptionOnRet(TRUE);
    Animate(0);
}

long C8085Display::ToggleLedWindow(UINT wparam,long lparam)
{
    m_bLEDButtonState = !m_bLEDButtonState;
    m_cToolBar.SetLedButtonState(m_bLEDButtonState);
    m_pLedDisplay->ShowWindow(m_bLEDButtonState);
    return 0;
}

long C8085Display::RefreshStackBox(UINT wparam,long lparam)
{
    DisplayStackDump();
    return 0;
}

long C8085Display::RefreshInstructionBox(UINT wparam,long lparam)
{
	Adjust();
    DisplayInstructions();
    return 0;
}

long C8085Display::OnVKeyDown(UINT wparam,long lparam)
{
    switch(wparam)
    {
    case VK_F5:
		if( m_bShiftDown )
			QuitSimulator( );
		else if( !m_bCtrlDown )
            OnButtonRun();
        break;

    case VK_F10:
        if(m_bCtrlDown)
            OnButtonRunto();
        else
            OnButtonStepOver();
        break;
    case VK_F11:
        if(m_bShiftDown)
            OnButtonStepOut();
        else
            StepInto();
        break;
    case VK_CONTROL:
        m_bCtrlDown=TRUE;
        break;
    case VK_SHIFT:
        m_bShiftDown=TRUE;
        break;
    case 3:
        if(m_bCtrlDown)
            OnButtonStop(); // stop the simulation 
    default:
        return 0;
    }
    return 1;
}

long C8085Display::OnVKeyUp(UINT wparam,long lparam)
{
    switch(wparam)
    {
    case VK_CONTROL:
        m_bCtrlDown=FALSE;
        break;
    case VK_SHIFT:
        m_bShiftDown=FALSE;
        break;
    default:
        return 0;
    }
    return 1;
}

void C8085Display::OnCancel()
{

}

void C8085Display::OnClose() 
{
	// TODO: Add your message handler code here and/or call default
    if(IsAnimating()) 
        return;	
	GetView()->Set8085DisplayDialogWindowPosition( );	// save for the next invokation
    OnOK();
    //CDialog::EndDialog(0);
}

void C8085Display::ChangeButtonState()
{
    BOOL bState = IsAnimating();

    m_cToolBar.GetToolBarCtrl().EnableButton(ID_BUTTON_STOP,bState); 
    m_cToolBar.GetToolBarCtrl().EnableButton(ID_BUTTON_RUN,!bState); 
    m_cToolBar.GetToolBarCtrl().EnableButton(ID_BUTTON_STEP_INTO,!bState); 
    m_cToolBar.GetToolBarCtrl().EnableButton(ID_BUTTON_STEP_OVER,!bState); 
    m_cToolBar.GetToolBarCtrl().EnableButton(ID_BUTTON_STEP_OUT,!bState); 
    m_cToolBar.GetToolBarCtrl().EnableButton(ID_BUTTON_RUNTO,!bState); 
    m_cToolBar.GetToolBarCtrl().EnableButton(ID_BUTTON_QUIT_SIMULATOR,!bState); 
    m_cToolBar.GetToolBarCtrl().EnableButton(ID_BUTTON_ANIMATE,!bState); 
	m_cToolBar.GetToolBarCtrl().EnableButton(ID_BUTTON_TOGGLE_BKPT,!bState);
	m_cToolBar.GetToolBarCtrl().EnableButton(ID_BUTTON_ENABLE_DISABLE_BKPT,!bState);
}

// show a triangle at the given address in the instruction box
// this corresponds to the line number in the edit window
void C8085Display::ShowDisAssemblyPosition(word address)
{
    m_Instructions.ShowDisAssemblyPosition(address);
}

void C8085Display::StepInto(BOOL bRedraw)
{
    if(IsAnimating()) 
        return;
    m_bExceptionCaught = FALSE;
    GetDocument()->SetAnimating(TRUE);
    // if debug info is available then this shows its position
    // in the '.85' file
    CMDIFrameWnd *pFrame = (CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
    // Get the active MDI child window.
    CMDIChildWnd *pChild = (CMDIChildWnd *) pFrame->GetActiveFrame();
    CMy8085View *pView= (CMy8085View *)pChild->GetActiveView();
    try
    {
        Get8085Object()->SimulateOneInstruction();
        if(bRedraw)
        {
            Adjust();
            Refresh();
            pView->ShowCurrentPosition();
        }
    }
    catch (CMyException& cm)
    {
        m_bExceptionCaught = TRUE;
        if(no_source_equivalent==cm.GetException())
            pView->StatusBarMessage(cm.GetErrorMessage());
        else
            MessageBox(cm.GetErrorMessage(),"Exception");
    }
    GetDocument()->SetAnimating(FALSE);
}

BOOL C8085Display::DestroyWindow() 
{
	// TODO: Add your specialized code here and/or call the base class
    ((CMy8085App *)AfxGetApp())->m_h8085Dialog = NULL;
	return CDialog::DestroyWindow();
}

void C8085Display::OnButtonToggleBkpt() 
{
	// TODO: Add your command handler code here
	GetView()->ToggleBkPtAtAddress( AddressAtIndex( ) );
	//		m_Instructions.AddressAtIndex( m_Instructions.GetSelectedIndex( ) ) );

//	m_Instructions.ToggleBreakPoint();
//	m_Instructions.InvalidateRect(NULL);
}

void C8085Display::OnButtonEnableDisableBkpt() 
{
	// TODO: Add your command handler code here
	GetView( )->EnableDisableBkPt( AddressAtIndex( ) );
	//m_Instructions.EnableDisableBkPt();
}

void C8085Display::QuitSimulator()
{
	// TODO: Add your command handler code here
    GetDocument()->SetAnimating(FALSE);
    ChangeButtonState();
    Refresh();
	// OnButtonQuitSimulator( );
	PostMessage( WM_CLOSE );
}

void C8085Display::OnAccelCloseSimulation() 
{
	// TODO: Add your command handler code here
	QuitSimulator( );	
}

word C8085Display::AddressAtIndex()
{
	return m_Instructions.AddressAtIndex(m_Instructions.GetSelectedIndex());
}
